package br.mikhas.reflector;

import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * Base class of Reflector library.
 * <p>
 * Reflection features can be easily accessed from this class using friendly
 * method names
 * 
 * @author Mikhail Domanoski
 * 
 */
public final class Reflect {

	private static Map<Class<?>, ClassProxy<?>> classProxyCache = new HashMap<Class<?>, ClassProxy<?>>(
			100);

	/**
	 * Using a private constructor to keep it an untility class
	 */
	private Reflect() {

	}

	/**
	 * Applies Refractor features to an instance of the Class
	 * 
	 * @param <T>
	 *            The type that the Class represents
	 * @param cls
	 *            The class to be reflected
	 * @return A proxy that facilitates the use of reflection over the Object
	 */
	public static <T> ObjectProxy<T> onInstance(Class<T> cls) {
		return on(on(cls).newInstance());
	}

	/**
	 * Applies Refractor features to an instance of a Class
	 * 
	 * @param cls
	 *            The full qualified name of the class to be reflected
	 * @return A proxy that facilitates the use of reflection over the Object
	 */
	public static ObjectProxy<?> onInstance(String cls) {
		return on(on(cls).newInstance());
	}

	/**
	 * Applies Refractor features to an Object
	 * 
	 * @param object
	 *            The object that will be wrapped into an Refractor proxy
	 * @return A proxy that facilitates the use of reflection over the Object
	 */
	public static <T> ObjectProxy<T> on(T object) {
		return new CoreObjectProxy<T>(object);
	}

	/**
	 * Applies Refractor features to a Class
	 * 
	 * @param cls
	 *            The Class that will be wrapped into an Refractor proxy
	 * @return A proxy that facilitates the use of reflection over the Class
	 */
	@SuppressWarnings("unchecked")
	public static <T> ClassProxy<T> on(Class<T> clazz) {

		ClassProxy<T> classProxy = (ClassProxy<T>) classProxyCache.get(clazz);
		if (classProxy == null) {
			classProxy = new CoreClassProxy<T>(clazz);
			classProxyCache.put(clazz, classProxy);
		}

		return classProxy;
	}

	/**
	 * Applies Refractor features to a Class
	 * 
	 * @param className
	 *            The name of the class that will be wrapped into an Refractor
	 *            proxy
	 * @return A proxy that facilitates the use of reflection over the Class
	 */
	public static ClassProxy<?> on(String className) {
		try {
			Class<?> cls = Class.forName(className);
			return on(cls);
		} catch (ClassNotFoundException e) {
			throw new ReflectionException("Could not find class: " + className,
					e);
		}
	}
}
